package org.study.bookmanager.service.impl;

import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.study.bookmanager.entity.Roles;
import org.study.bookmanager.entity.UserType;
import org.study.bookmanager.entity.Users;
import org.study.bookmanager.io.reponse.RestBean;
import org.study.bookmanager.io.reponse.User.LoginResp;
import org.study.bookmanager.io.reponse.User.RegisterResp;
import org.study.bookmanager.io.reponse.User.UserInfoResp;
import org.study.bookmanager.io.request.User.LoginReq;
import org.study.bookmanager.io.request.User.RegisterReq;
import org.study.bookmanager.io.request.User.UpdateUserInfoReq;
import org.study.bookmanager.repo.UsersRespository;
import org.study.bookmanager.service.UserService;
import org.study.bookmanager.utils.CopyUtils;
import org.study.bookmanager.utils.JwtUtils;

import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.AccessDeniedException;
import java.util.Date;

@Service
@Slf4j
public class UserServiceImpl implements UserService {

    @Resource
    private AuthenticationManager authenticationManager;

    @Resource
    private UsersRespository usersRespository;

    @Resource
    private PasswordEncoder passwordEncoder;


    public static void handleLoginProcess(HttpServletRequest request,
                                          HttpServletResponse response,
                                          Object exceptionOrAuthentication) throws IOException {
        response.setContentType("application/json;charset=utf-8");
        PrintWriter writer = response.getWriter();
        if(exceptionOrAuthentication instanceof AccessDeniedException exception) {
            writer.write(RestBean.failure(403, "拒绝访问"));
        } else if(exceptionOrAuthentication instanceof AuthenticationException exception) {
            writer.write(RestBean.failure(401, "账号或密码错误"));
        } else if(exceptionOrAuthentication instanceof Authentication authentication){
            //不过这里需要注意，在登录成功的时候需要返回我们生成的JWT令牌，这样客户端下次访问就可以携带这个令牌了，令牌过期之后就需要重新登录才可以
            Object principal = authentication.getPrincipal();
            if (principal instanceof UserDetails) {
                UserDetails userDetails = (UserDetails) principal;
                writer.write(RestBean.success(
                                new LoginResp(
                                        JwtUtils.createJWT(
                                                JwtUtils.createClaims(userDetails.getUsername())//我真的无语了authentication.getPrincipal()居然返回了userDetail
                                        )
                                )
                )
             );
            } else {
                writer.write(RestBean.failure(RestBean.Code.服务错误.ordinal(), RestBean.Code.服务错误.name()));
            };
        }
    }

    /**
     * @param req
     * @param request
     * @param response
     * @return
     */
    @Override
    public void login(LoginReq req, HttpServletRequest request, HttpServletResponse response) throws IOException{
        try {
            Authentication authentication = authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(req.getUsername(),req.getPassword()));
            handleLoginProcess(request, response, authentication);
        } catch (AuthenticationException e) {
            handleLoginProcess(request, response, e);
        }
    }

    /**
     * @param req
     * @return
     */
    @Override
    public Object register(RegisterReq req){
        if(usersRespository.existsByuserName(req.getUsername())){
            return RestBean.Code.用户名已存在;
        }
        Users user=Users.builder().
                userName(req.getUsername()).
                passwordHash(passwordEncoder.encode(req.getPassword())).
                registerDate(new Date()).
                role(new Roles()).
                type(new UserType()).
                build();
        usersRespository.save(user);

        return RegisterResp.builder().token(JwtUtils.createJWT(JwtUtils.createClaims(user.getUserName()))).
                build();
    }

    /**
     * @param userName
     * @return
     */
    @Override
    public Object getUserInfo(String userName){
        if(!usersRespository.existsByuserName(userName)){
            return RestBean.Code.不存在的用户;
        }
        UserInfoResp.UserInfoRespBuilder builder = UserInfoResp.builder();//对于需要构造结构体的提前使用builder，不需要的话另作处理
        Users user = usersRespository.findByuserName(userName);
        if(user!=null){
            UserType userType = user.getType();
            if(userType!=null){
                builder.user_type(userType.getName());
            }
            Roles role = user.getRole();
            if(role!=null){
                builder.role(role.getRoleName());
            }
            return builder.
                    email(user.getEmail()).
                    contact(user.getContact()).
                    name(user.getName()).id(user.getUserID()).register_date(user.getRegisterDate()).build();
        }else{
            return RestBean.Code.服务错误;
        }
    }

    /**
     * @param req
     * @return
     */
    @Override
    public Object updateUserInfo(UserDetails details,UpdateUserInfoReq req) {
        String username=details.getUsername();
        Users userReq=Users.builder().passwordHash(req.getPassword_hash()).
                email(req.getEmail()).
                contact(req.getContact()).
                name(req.getName()).build();
        Users user=usersRespository.findByuserName(username);
        if(user!=null){
            CopyUtils.copyProperties(userReq,user);
            usersRespository.save(user);
        }else{
            return RestBean.Code.用户登陆状态异常;
        }
        return RestBean.ok;
    }

    /**
     * @return
     */
    @Override
    public Object getNotice(UserDetails details){
        return RestBean.unImpl;
    }
}
